유형 감잡기 🟢 연결요소 🟢 단지번호 🟡 미로탐색 🟡 토마토 🔴 숨바꼭질 🔴 유기농배추 🏁 마무리
📋 목차
🗺️ 코딩테스트 실전 · 유형 01

코딩테스트 실전 — 그래프 탐색(BFS·DFS)

코테에서 가장 자주 나오는 유형이 그래프 탐색이에요. 격자 최단거리, 연결 요소 세기, 도달 가능성 판별… 겉모습은 달라도 뼈대는 BFS/DFS 하나예요. 난이도별 6문제를 먼저 스스로 풀고, 힌트·정답 자바 코드로 확인해봐요.

🎯 이 페이지를 끝내면
🧑‍💻
자바는 브라우저에서 실행이 안 돼요. 그래서 각 문제는 ① 문제 → ② 입출력 예시 → ③ 힌트(펼치기) → ④ 정답 코드+해설(펼치기) → ⑤ 복잡도 → ⑥ 채점 링크 순서로 구성했어요. 정답은 처음엔 접혀 있어요. 먼저 종이/IDE에서 직접 짜본 뒤 펼쳐서 맞춰보는 게 실력이 가장 빨리 늘어요.
🧭
유형 감잡기
이런 문장이 보이면 그래프 탐색이에요
문제 유형을 알아채는 게 절반이에요.

그래프 탐색은 "연결된 것들을 빠짐없이 훑는" 알고리즘이에요. 정점과 간선으로 된 그래프뿐 아니라, 격자(2차원 배열)도 "칸=정점, 상하좌우 이웃=간선"인 그래프로 보면 똑같이 풀려요.

🔔 유형 시그널 — 이런 말이 나오면 의심 · 최단 거리 / 최소 이동 횟수 — "몇 칸 만에", "최소 몇 번" → 가중치 없으면 BFS.
· 덩어리(영역)의 개수 — "단지", "섬", "그룹", "연결 요소가 몇 개" → 방문 안 한 칸마다 탐색 1번 = 컴포넌트 세기.
· 도달 가능성 / 전부 퍼지기 — "모두 도달할 수 있나", "며칠이면 다 익나" → 시작점에서 퍼뜨리는 BFS.
· 상하좌우로 이동 — 격자에서 인접 칸으로 움직이는 모든 문제. 💡 "최단"이라는 단어가 보이고 이동 비용이 전부 1이면, 열에 아홉은 BFS예요.

BFS vs DFS — 한눈에

🌊 BFS (너비 우선)
가까운 칸부터 동심원처럼 퍼져나가요. 큐(FIFO) 사용. 처음 도달한 순간이 곧 최단 거리라서 "최소 이동" 문제에 강해요.
🕳️ DFS (깊이 우선)
한 방향으로 갈 수 있는 데까지 파고들었다 막히면 되돌아와요. 스택/재귀 사용. "영역 세기", "모든 경로 탐색"에 간결해요.
🔤
방문 배열(visited) — 이미 다녀간 칸을 표시해 두는 배열이에요. 이게 없으면 같은 칸을 무한히 오가며 프로그램이 멈추지 않아요. BFS/DFS의 필수 부품이에요.

격자 4방향 탐색 템플릿 (자바)

거의 모든 격자 문제가 이 뼈대에서 시작해요. dx/dy 한 쌍으로 상하좌우를 한 번에 훑어요.

int[] dx = {-1, 1, 0, 0};   // 상, 하
int[] dy = {0, 0, -1, 1};   // 좌, 우

// (x, y)에서 네 방향 이웃 살펴보기 (BFS 안쪽)
for (int d = 0; d < 4; d++) {
    int nx = x + dx[d];
    int ny = y + dy[d];
    if (nx < 0 || ny < 0 || nx >= N || ny >= M) continue;  // ① 범위 밖이면 건너뜀
    if (visited[nx][ny] || map[nx][ny] == 0) continue;        // ② 이미 갔거나 벽이면 건너뜀
    visited[nx][ny] = true;             // ③ 큐에 "넣을 때" 방문표시! (중복 방지)
    queue.add(new int[]{nx, ny});
}
⚠️
방문표시는 큐에 "넣을 때". 꺼낼 때 표시하면 같은 칸이 큐에 여러 번 들어가 시간초과·오답이 나요. 넣는 순간 바로 visited=true가 그래프 탐색의 철칙이에요.
🖼️ 그림으로 보기 — BFS는 거리(층)별로 퍼진다
S 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6
칸 안 숫자는 S에서 몇 칸 떨어졌는지예요. BFS는 거리 0 → 1 → 2 …순으로, 같은 거리를 모두 처리한 뒤 다음 층으로 가요(강조된 대각선이 "거리 3" 층). 그래서 목적지에 처음 닿는 순간이 곧 최단 거리예요.
1
문제 1 🟢 초급
연결 요소의 개수
그래프에서 "따로 떨어진 덩어리"가 몇 개인지 세기.
백준 11724DFS / BFS그래프 · 연결 요소

① 문제. 정점 N개, 간선 M개짜리 방향 없는 그래프가 주어질 때, 연결 요소(서로 이어진 정점 덩어리)의 개수를 구하세요. 첫 줄에 N M, 다음 M줄에 간선의 두 끝 정점 u v가 주어져요.

② 입출력 예시.

입력
6 5
1 2
2 5
5 1
3 4
4 6
출력
2

{1,2,5} 하나, {3,4,6} 하나 → 총 2개 덩어리.

💡 힌트 — 펼쳐 보기
인접 리스트로 그래프를 만들고, 모든 정점을 한 번씩 훑으며 "아직 방문 안 한 정점"을 만날 때마다 거기서 DFS(또는 BFS)를 한 번 돌려요. 탐색을 시작한 횟수가 곧 덩어리 개수예요. 방향이 없으니 간선은 u→v, v→u 양쪽에 넣어요.
▲ 접기
✅ 정답 코드 + 해설 — 펼쳐 보기 (먼저 풀어보세요!)
import java.io.*;
import java.util.*;

public class Main {
    static List<Integer>[] graph;
    static boolean[] visited;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        graph = new ArrayList[n + 1];               // 1번~n번 정점
        for (int i = 1; i <= n; i++) graph[i] = new ArrayList<>();

        for (int i = 0; i < m; i++) {
            st = new StringTokenizer(br.readLine());
            int u = Integer.parseInt(st.nextToken());
            int v = Integer.parseInt(st.nextToken());
            graph[u].add(v);            // 방향 없는 그래프 → 양쪽에 추가
            graph[v].add(u);
        }

        visited = new boolean[n + 1];
        int count = 0;
        for (int i = 1; i <= n; i++) {
            if (!visited[i]) {          // 새로운 덩어리의 출발점
                count++;
                dfs(i);
            }
        }
        System.out.println(count);
    }

    static void dfs(int cur) {
        visited[cur] = true;
        for (int next : graph[cur]) {
            if (!visited[next]) dfs(next);
        }
    }
}
해설. 핵심은 바깥 for문이에요. 정점 1번부터 n번까지 보면서 아직 방문 안 한 정점을 만나면, 그 정점이 새 덩어리의 시작이라는 뜻이니 count++ 후 DFS로 그 덩어리 전체를 방문 처리해요. 한 번 방문한 덩어리는 다음 반복에서 visited에 걸려 건너뛰어지므로, 탐색을 새로 시작한 횟수 = 연결 요소 개수가 돼요. N이 최대 1000이라 재귀 깊이도 안전해요.
▲ 접기

⑤ 복잡도. 각 정점·간선을 한 번씩만 봐요 → 시간 O(N + M), 공간 O(N + M)(인접 리스트).

⚖️ 백준 11724번에서 채점하기 (새 탭 ↗)
2
문제 2 🟢 초급
단지번호붙이기 (섬의 개수)
격자에서 1로 이어진 덩어리 개수와 각 크기 구하기.
백준 2667격자 DFS / BFS연결 요소 + 크기

① 문제. N×N 격자에 집(1)과 빈 땅(0)이 있어요. 상하좌우로 이어진 집들을 하나의 단지라 할 때, 단지 수각 단지의 집 수를 오름차순으로 출력하세요. (첫 줄 N, 다음 N줄은 공백 없이 0/1이 붙어 있음.)

② 입출력 예시.

입력
7
0110100
0110101
1110101
0000111
0100000
0111110
0111000
출력
3
7
8
9
💡 힌트 — 펼쳐 보기
문제 1의 "덩어리 세기"를 격자 버전으로 옮긴 거예요. 격자 전체를 두 겹 for문으로 훑다가 방문 안 한 집(1)을 만나면 거기서 BFS/DFS를 한 번 돌리고, 그때 몇 칸을 방문했는지 세어 그 단지의 크기로 기록해요. 4방향 템플릿에 count만 얹으면 끝. 마지막에 크기 목록을 정렬해서 출력해요.
▲ 접기
✅ 정답 코드 + 해설 — 펼쳐 보기 (먼저 풀어보세요!)
import java.io.*;
import java.util.*;

public class Main {
    static int n;
    static int[][] map;
    static boolean[][] visited;
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine().trim());
        map = new int[n][n];
        for (int i = 0; i < n; i++) {
            String line = br.readLine();
            for (int j = 0; j < n; j++) {
                map[i][j] = line.charAt(j) - '0';   // 붙어있는 숫자를 한 글자씩
            }
        }

        visited = new boolean[n][n];
        List<Integer> sizes = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (map[i][j] == 1 && !visited[i][j]) {
                    sizes.add(bfs(i, j));           // 새 단지 → 크기 기록
                }
            }
        }

        Collections.sort(sizes);                    // 오름차순
        StringBuilder sb = new StringBuilder();
        sb.append(sizes.size()).append('\n');
        for (int s : sizes) sb.append(s).append('\n');
        System.out.print(sb);
    }

    static int bfs(int si, int sj) {
        Queue<int[]> q = new LinkedList<>();
        q.add(new int[]{si, sj});
        visited[si][sj] = true;
        int count = 0;
        while (!q.isEmpty()) {
            int[] cur = q.poll();
            count++;                                // 꺼낼 때 집 1채 카운트
            for (int d = 0; d < 4; d++) {
                int nx = cur[0] + dx[d];
                int ny = cur[1] + dy[d];
                if (nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
                if (map[nx][ny] == 1 && !visited[nx][ny]) {
                    visited[nx][ny] = true;         // 넣을 때 방문표시
                    q.add(new int[]{nx, ny});
                }
            }
        }
        return count;
    }
}
해설. bfs방문한 칸 수를 리턴하게 만든 게 포인트예요. 큐에서 꺼낼 때마다 count++하면 그 단지의 집 수가 나와요. 입력이 0110100처럼 공백 없이 붙어 있으니 line.charAt(j) - '0'로 한 글자씩 숫자로 바꿔요. 마지막에 Collections.sort로 오름차순 정렬 후 출력. DFS로 짜도 되지만, 격자가 커질 때 재귀 깊이가 부담되면 BFS가 안전해요.
▲ 접기

⑤ 복잡도. 모든 칸을 한 번씩 방문 → 시간 O(N²), 공간 O(N²).

⚖️ 백준 2667번에서 채점하기 (새 탭 ↗)
3
문제 3 🟡 중급
미로 탐색 (격자 최단거리)
출발에서 도착까지 지나는 최소 칸 수 구하기 — 전형적인 BFS.
백준 2178격자 BFS최단 거리

① 문제. N×M 미로에서 1은 이동 가능, 0은 벽이에요. (1,1)에서 (N,M)까지 상하좌우로 이동할 때 지나야 하는 최소 칸 수를 구하세요. (시작·도착 칸 모두 셈에 포함.)

② 입출력 예시.

입력
4 6
101111
101010
101011
111011
출력
15
💡 힌트 — 펼쳐 보기
"최소 칸 수" = 가중치 없는 최단 거리BFS예요. visited 대신 거리 배열 dist를 쓰면 방문 여부(dist==0이면 미방문)와 거리를 동시에 관리할 수 있어 편해요. 이웃으로 퍼질 때 dist[다음] = dist[현재] + 1. 시작 칸을 1로 두면 도착 칸의 dist가 곧 답이에요.
▲ 접기
✅ 정답 코드 + 해설 — 펼쳐 보기 (먼저 풀어보세요!)
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        int[][] map = new int[n][m];
        for (int i = 0; i < n; i++) {
            String line = br.readLine();
            for (int j = 0; j < m; j++) map[i][j] = line.charAt(j) - '0';
        }

        int[][] dist = new int[n][m];     // dist==0 이면 아직 미방문
        int[] dx = {-1, 1, 0, 0};
        int[] dy = {0, 0, -1, 1};

        Queue<int[]> q = new LinkedList<>();
        q.add(new int[]{0, 0});
        dist[0][0] = 1;                   // 시작 칸도 1칸으로 셈

        while (!q.isEmpty()) {
            int[] cur = q.poll();
            int x = cur[0], y = cur[1];
            for (int d = 0; d < 4; d++) {
                int nx = x + dx[d], ny = y + dy[d];
                if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
                if (map[nx][ny] == 1 && dist[nx][ny] == 0) {
                    dist[nx][ny] = dist[x][y] + 1;   // 한 칸 더
                    q.add(new int[]{nx, ny});
                }
            }
        }
        System.out.println(dist[n - 1][m - 1]);
    }
}
해설. BFS는 가까운 칸부터 층층이 퍼지므로, 어떤 칸에 처음 도달했을 때의 거리가 곧 최단이에요. 그래서 dist[nx][ny] == 0(아직 안 감)일 때만 값을 채우고 다시는 안 건드려요. 시작을 1로 뒀으니 도착 칸 값이 그대로 "지나온 칸 수"예요. 만약 시작을 0으로 두면 마지막에 +1을 해야 하니 주의!
▲ 접기

⑤ 복잡도. 각 칸을 한 번씩 큐에 넣고 뺌 → 시간 O(N·M), 공간 O(N·M).

⚖️ 백준 2178번에서 채점하기 (새 탭 ↗)
4
문제 4 🟡 중급
토마토 (다중 시작점 BFS)
시작점이 여러 개! 큐에 한꺼번에 넣고 동시에 퍼뜨리기.
백준 7576다중 시작점 BFS동시 확산

① 문제. M×N 상자에 익은 토마토(1), 안 익은 토마토(0), 빈 칸(-1)이 있어요. 하루가 지나면 익은 토마토의 상하좌우 토마토도 익어요. 모두 익는 데 걸리는 최소 일수를 구하세요. 이미 다 익었으면 0, 끝내 다 못 익으면 -1. (첫 줄 M N = 가로·세로.)

② 입출력 예시.

입력
6 4
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 1
출력
8

익은 토마토가 여러 개면 전부 동시에 퍼져요. 위 예시는 한 개뿐이라 가장 먼 칸까지 8일 걸려요.

💡 힌트 — 펼쳐 보기
핵심은 "처음부터 익은 토마토를 전부 큐에 넣고 시작"하는 거예요(다중 시작점 BFS). 그러면 여러 지점에서 동시에 물결이 퍼져요. 칸 값에 날짜를 누적(box[다음] = box[현재] + 1)하고, 끝나면 전체에서 가장 큰 값 − 1이 답이에요(시작을 1로 뒀으니). 다 돌린 뒤에도 0이 남아 있으면 -1.
▲ 접기
✅ 정답 코드 + 해설 — 펼쳐 보기 (먼저 풀어보세요!)
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int m = Integer.parseInt(st.nextToken());   // 가로(열)
        int n = Integer.parseInt(st.nextToken());   // 세로(행)

        int[][] box = new int[n][m];
        Queue<int[]> q = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < m; j++) {
                box[i][j] = Integer.parseInt(st.nextToken());
                if (box[i][j] == 1) q.add(new int[]{i, j});  // 익은 것 전부 시작점!
            }
        }

        int[] dx = {-1, 1, 0, 0};
        int[] dy = {0, 0, -1, 1};
        while (!q.isEmpty()) {
            int[] cur = q.poll();
            int x = cur[0], y = cur[1];
            for (int d = 0; d < 4; d++) {
                int nx = x + dx[d], ny = y + dy[d];
                if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
                if (box[nx][ny] == 0) {                 // 안 익은 칸만
                    box[nx][ny] = box[x][y] + 1;        // 며칠째인지 누적
                    q.add(new int[]{nx, ny});
                }
            }
        }

        int max = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (box[i][j] == 0) {         // 끝까지 못 익은 칸 → 불가능
                    System.out.println(-1);
                    return;
                }
                max = Math.max(max, box[i][j]);
            }
        }
        System.out.println(max - 1);          // 시작을 1로 뒀으니 -1
    }
}
해설. 시작점을 하나만 넣는 보통 BFS와 달리, 여기선 익은 토마토를 모조리 큐에 먼저 넣어요. 그러면 여러 물결이 동시에 퍼지면서 각 칸에 "며칠에 익었는지"가 자동으로 최소값으로 기록돼요. 값이 커질수록 늦게 익은 것이니 최댓값이 전체가 익는 날이고, 시작을 1로 뒀으므로 -1 보정해요. -1(빈 칸)은 조건 == 0에 안 걸려 자연히 무시돼요. 처음부터 0이 하나도 없으면 max가 1이라 답은 0이 돼요.
▲ 접기

⑤ 복잡도. 각 칸을 한 번씩만 처리 → 시간 O(N·M), 공간 O(N·M).

⚖️ 백준 7576번에서 채점하기 (새 탭 ↗)
5
문제 5 🔴 고급
숨바꼭질 (1차원 좌표 BFS)
격자가 아니어도 BFS! "상태"가 정점, "이동"이 간선.
백준 16971차원 BFS상태 공간 탐색

① 문제. 수빈이는 점 N에, 동생은 점 K에 있어요(0 ≤ N, K ≤ 100,000). 수빈이는 1초에 X-1, X+1, 또는 X×2 로 이동할 수 있어요. 동생을 찾는 가장 빠른 시간(초)을 구하세요.

② 입출력 예시.

입력
5 17
출력
4

5 → 10 → 9 → 18 → 17, 총 4초. (5→10 은 ×2, 10→9 는 −1, 9→18 은 ×2, 18→17 은 −1)

💡 힌트 — 펼쳐 보기
격자가 아니라도 "현재 위치 = 정점, 갈 수 있는 다음 위치 = 간선"으로 보면 똑같은 그래프예요. 각 위치에서 이웃은 x-1, x+1, x*2 세 개. 1초당 비용이 모두 같으니 BFS로 최단 시간을 구해요. 위치 0~100000visited/dist 배열로 관리하고, 범위(0 ≤ nx ≤ 100000)를 꼭 체크하세요. x*2 때문에 위로 갔다가 -1로 다시 내려오는 경로가 최단일 수 있어요.
▲ 접기
✅ 정답 코드 + 해설 — 펼쳐 보기 (먼저 풀어보세요!)
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int k = Integer.parseInt(st.nextToken());

        final int MAX = 100001;
        int[] dist = new int[MAX];
        Arrays.fill(dist, -1);          // -1 = 아직 방문 안 함

        Queue<Integer> q = new LinkedList<>();
        q.add(n);
        dist[n] = 0;                    // 시작점, 0초

        while (!q.isEmpty()) {
            int x = q.poll();
            if (x == k) break;          // 동생을 찾으면 종료
            int[] next = {x - 1, x + 1, x * 2};
            for (int nx : next) {
                if (nx < 0 || nx >= MAX) continue;   // 범위 밖 차단
                if (dist[nx] == -1) {                 // 처음 도달 = 최단
                    dist[nx] = dist[x] + 1;
                    q.add(nx);
                }
            }
        }
        System.out.println(dist[k]);
    }
}
해설. "1초당 비용이 동일한 최단 시간" → BFS예요. 각 위치를 처음 방문할 때의 dist가 곧 최소 초. x*2가 있어 위치가 N을 넘어갈 수 있으니 배열을 100001까지 잡고 범위 체크를 반드시 해요(안 하면 배열 밖 접근으로 터짐). N == Kdist[k]가 처음부터 0이라 그대로 0이 출력돼요. DP로도 풀리지만, "최소 횟수"라는 시그널 그대로 BFS가 가장 자연스러워요.
▲ 접기

⑤ 복잡도. 위치 수를 V=100,001이라 하면 각 위치를 한 번씩 방문 → 시간 O(V), 공간 O(V).

⚖️ 백준 1697번에서 채점하기 (새 탭 ↗)
6
문제 6 🔴 고급
유기농 배추 (여러 컴포넌트 + 여러 테스트케이스)
좌표로 심는 격자 + 테스트케이스마다 배열 초기화가 함정.
백준 1012격자 BFS / DFS연결 요소 · 다중 TC

① 문제. 가로 M, 세로 N 밭에 배추가 K개 심겨 있어요(위치 X Y로 주어짐). 인접(상하좌우)한 배추 무리마다 지렁이 1마리면 충분할 때, 필요한 최소 지렁이 수(= 배추 덩어리 개수)를 구하세요. 첫 줄에 테스트케이스 수 T가 주어져요.

② 입출력 예시. (문제 지문의 예시 밭 — 5마리)

입력
1
10 6 14
0 0
1 0
1 1
4 2
4 3
2 4
3 4
7 4
8 4
9 4
4 5
7 5
8 5
9 5
출력
5
💡 힌트 — 펼쳐 보기
알고리즘 자체는 문제 2(단지번호)와 똑같이 "격자 연결 요소 세기"예요. 다만 두 가지 함정이 있어요. ① 배추는 좌표 (X, Y)로 심어요field[X][Y] = 1처럼 넣고, 격자 크기·이동 시 X0~M-1, Y0~N-1 범위를 헷갈리지 마세요. ② 테스트케이스마다 밭·방문 배열을 새로 만들어 초기화해야 이전 케이스가 안 섞여요.
▲ 접기
✅ 정답 코드 + 해설 — 펼쳐 보기 (먼저 풀어보세요!)
import java.io.*;
import java.util.*;

public class Main {
    static int m, n;                 // m=가로(X범위), n=세로(Y범위)
    static int[][] field;
    static boolean[][] visited;
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int t = Integer.parseInt(br.readLine().trim());
        StringBuilder sb = new StringBuilder();

        while (t-- > 0) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            m = Integer.parseInt(st.nextToken());
            n = Integer.parseInt(st.nextToken());
            int k = Integer.parseInt(st.nextToken());

            field = new int[m][n];          // 테스트케이스마다 새로!
            visited = new boolean[m][n];
            for (int i = 0; i < k; i++) {
                st = new StringTokenizer(br.readLine());
                int x = Integer.parseInt(st.nextToken());
                int y = Integer.parseInt(st.nextToken());
                field[x][y] = 1;            // (X, Y) 자리에 배추
            }

            int worms = 0;
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (field[i][j] == 1 && !visited[i][j]) {
                        worms++;            // 새 덩어리 → 지렁이 1마리
                        bfs(i, j);
                    }
                }
            }
            sb.append(worms).append('\n');
        }
        System.out.print(sb);
    }

    static void bfs(int si, int sj) {
        Queue<int[]> q = new LinkedList<>();
        q.add(new int[]{si, sj});
        visited[si][sj] = true;
        while (!q.isEmpty()) {
            int[] cur = q.poll();
            for (int d = 0; d < 4; d++) {
                int nx = cur[0] + dx[d], ny = cur[1] + dy[d];
                if (nx < 0 || ny < 0 || nx >= m || ny >= n) continue;
                if (field[nx][ny] == 1 && !visited[nx][ny]) {
                    visited[nx][ny] = true;
                    q.add(new int[]{nx, ny});
                }
            }
        }
    }
}
해설. 뼈대는 문제 2와 같아요. 방문 안 한 배추를 만날 때마다 worms++ 후 BFS로 그 덩어리를 통째로 방문 처리 → 덩어리 수 = 지렁이 수. 실수 포인트 둘: (1) 입력이 좌표 X Yfield[x][y] 인덱싱과 범위(x<m, y<n)를 일치시켜야 하고, (2) 매 테스트케이스마다 field·visited를 새로 할당해 이전 결과가 남지 않게 해야 해요. 여러 케이스라 출력은 StringBuilder에 모아 한 번에 찍으면 빨라요.
▲ 접기

⑤ 복잡도. 테스트케이스 하나당 모든 칸 1회 방문 → 시간 O(M·N), 공간 O(M·N).

⚖️ 백준 1012번에서 채점하기 (새 탭 ↗)
🔁
비슷한 유형 더 있어요. 도달 가능성을 세는 바이러스(2606), 트리에서 두 사람 사이 거리를 재는 촌수계산(2644)도 이 문제와 뼈대가 같아요. 하나를 확실히 익히면 나머지는 변형일 뿐이에요.
🧠 이 유형 핵심 요약
🏁
마무리 · 체크리스트
제출 전 이 5가지를 꼭 확인해요
그래프 탐색에서 감점·오답이 가장 자주 나는 지점들이에요.
✅ 흔한 실수 체크리스트 ① 방문 배열 필수. 사이클이 있으면 방문표시 없이는 무한 반복에 빠져요.
② BFS는 큐에 넣을 때 방문표시. 꺼낼 때 표시하면 같은 칸이 큐에 여러 번 들어가 시간초과/오답.
③ 좌표 범위 체크. nx/ny0 ≤ nx < N 안에 드는지 이웃 접근 전에 검사(배열 밖 접근 방지).
④ 행·열(N·M)과 좌표(X·Y) 혼동 주의. 문제마다 "가로가 먼저"인지 "세로가 먼저"인지 다르니 입력 순서를 꼭 확인.
⑤ 초기화·시작값. 다중 테스트케이스는 배열 재초기화, 최단거리는 시작을 0으로 둘지 1로 둘지에 따라 마지막 ±1 보정. 💡 DFS 재귀는 격자가 아주 크면 StackOverflow가 날 수 있어요. 그럴 땐 BFS(큐)로 바꾸면 안전해요.

🔗 더 풀어볼 문제

🎯
다음 목표. 위 6문제를 정답을 안 보고 스스로 다시 짤 수 있으면 이 유형은 합격이에요. 그 다음은 최단거리에 가중치가 붙는 다익스트라, 상태에 정보가 더해지는 3차원 BFS(2206)로 확장해봐요.